﻿#include "oglwidget.h"
#include <QMouseEvent>
#include <math.h>
#include <QDebug>
#include <QFile>
#include <QTextStream>
#include <QMessageBox>
#include <QtMath>
#include <QDir>
#include <QColorDialog>
#include <QTableWidget>
#include <QVector4D>
#include <QPainter>
#include <iostream>
#include <random>
#include <set>
#include <glm/gtc/type_ptr.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <vcg/complex/algorithms/clean.h>
#include <vcg/simplex/face/jumping_pos.h>
#include <wrap/io_trimesh/export_obj.h>

#include "global.h"
#include "filter.h"
#include "window.h"

#pragma comment(lib,"opengl32.lib")     //for glPolygonMode...

OglWidget::OglWidget(QWidget *parent,bool nf, bool of):
    QOpenGLWidget(parent),
    newFirst(nf),openFirst(of),
    m_ebo(QOpenGLBuffer::IndexBuffer)    //这句很重要，如果没有，使用glDrawElements会导致程序异常结束，因为默认初始化为顶点缓冲类型
{
    setFocusPolicy(Qt::ClickFocus);
    //make trivial axes cross
    _axesLines.push_back(std::make_pair(QVector3D(0.0, 0.0, 0.0), QColor(1.0, 0.0, 0.0)));
    _axesLines.push_back(std::make_pair(QVector3D(1.0, 0.0, 0.0), QColor(1.0, 0.0, 0.0)));
    _axesLines.push_back(std::make_pair(QVector3D(0.0, 0.0, 0.0), QColor(0.0, 1.0, 0.0)));
    _axesLines.push_back(std::make_pair(QVector3D(0.0, 1.0, 0.0), QColor(0.0, 1.0, 0.0)));
    _axesLines.push_back(std::make_pair(QVector3D(0.0, 0.0, 0.0), QColor(0.0, 0.0, 1.0)));
    _axesLines.push_back(std::make_pair(QVector3D(0.0, 0.0, 1.0), QColor(0.0, 0.0, 1.0)));

    _pureSurfaceColor.setRgbF(1.0f,0.5f,0.31f);

    this->resize(parent->width()*0.8,parent->height());
}

OglWidget::~OglWidget(){
   cleanup();
}

void OglWidget::cleanup()
{
    makeCurrent();
    m_ebo.destroy();
    m_vbo.destroy();
    m_vao.destroy();

    if(gridsVBO.isCreated()){
        gridsVBO.destroy();
    }

    if(cubeVBO.isCreated()){
        cubeVBO.destroy();
    }

    if(axesVBO.isCreated()){
        axesVBO.destroy();
    }

    if(shaderProgram != nullptr){
        delete shaderProgram;
        shaderProgram=nullptr;
    }

    doneCurrent();
}

/*
 * scene render core functions
 */

void OglWidget::initializeGL()
{
    connect(context(),&QOpenGLContext::aboutToBeDestroyed,this,&OglWidget::cleanup);
    initializeOpenGLFunctions();

    setGLSL(LIGHT_DEFAULT);    //default GLSL

    _setBaseVertices();
    _setBaseShaderProgram();

    m_vao.create();
    m_vbo.create();
    m_ebo.create();

    if(newFirst&&!openFirst){
        //must be these two conditions
        setDefaultObject();
    }

    this->setCamera(&llxCamera);
    llxInteractor.setCamera(&llxCamera);
    llxAnimator.setInteractor(&llxInteractor);
}

void OglWidget::resizeGL(int w, int h)
{
    glViewport(0,0,w,h);
    if(0==h){
        h = 1;
    }
    GLfloat ratio = w/(GLfloat)h;
    GLfloat m_field = 45.0f;  //perspective verticalAngle

    pMat.setToIdentity();
    pMat.perspective(m_field,ratio,0.01f,1000.0f);

    llxInteractor.setScreenSize(w,h);
    llxAnimator.setScreenSize(w,h);
}

void OglWidget::paintGL()
{
    glClearColor(_bgColor.redF(),_bgColor.greenF(),_bgColor.blueF(),1.0f);
    glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
    glClearDepth(1.0f);

    llxAnimator.animate();
    llxInteractor.tupdate();

    glm::mat4 tmpVMat = llxCamera.getMatrix();
    vMat = QMatrix4x4(tmpVMat[0][0],tmpVMat[1][0],tmpVMat[2][0],tmpVMat[3][0],
                      tmpVMat[0][1],tmpVMat[1][1],tmpVMat[2][1],tmpVMat[3][1],
                      tmpVMat[0][2],tmpVMat[1][2],tmpVMat[2][2],tmpVMat[3][2],
                      tmpVMat[0][3],tmpVMat[1][3],tmpVMat[2][3],tmpVMat[3][3]);

    mvStack.push(vMat*mMat);
    glEnable(GL_DEPTH_TEST);

    drawScene();
    _drawBaseObjects();

    glDisable(GL_DEPTH_TEST);
    mvStack.pop();
}

void OglWidget::paintEvent(QPaintEvent *event){
    Q_UNUSED(event);

    makeCurrent();
    paintGL();

    if(_drawInfos2d==true){
        QString str = "Number of Stems: "+skeletonInfos[0] +
                      "\nNumber of Bifurcations: "+skeletonInfos[1] +
                      "\nNumber of Branches: " + skeletonInfos[2] +
                      "\nTotal Fragmentation: " + skeletonInfos[3] +
                      "\nTotal Length: " + skeletonInfos[4] +
                      "\nAverage Diameter: " + skeletonInfos[5];

        int W = this->width();
        int H = this->height();
        QRect rect(0,0,W,H);

        QPainter painter(this);
        painter.setPen(Qt::lightGray);

        painter.drawText(rect,Qt::AlignBottom/*|Qt::AlignRight*/, str);
        painter.drawRect(rect);
    }
}


//private functions
void OglWidget::setGLSL(int programID)
{
    makeCurrent();

    if(shaderProgram!=nullptr)
        delete shaderProgram;

    shaderProgram = new QOpenGLShaderProgram;

    bool success = false;

    switch(programID)
    {
    case LIGHT_DEFAULT:
    {
        emit _setInfoPanel("Default light\n");
        shader_path = ":/shaders/default";
        success = createShaderProgram(shaderProgram,shader_path);
        if(!success){ return; }
        break;
    }
    case LIGHT_DIFFUSE:
    {
        emit _setInfoPanel("Diffuse light\n");
        if(_geometry){
            shader_path = ":/geometrys/gdiffuse";
            success = createShaderProgram(shaderProgram,shader_path,true);
            qDebug()<<"has geometry";
        }else
        {
            shader_path = ":/shaders/diffuse";
            success = createShaderProgram(shaderProgram,shader_path);
            qDebug()<<"no geometry";
        }

        if(!success){return;}
        break;

    }
    case LIGHT_DIRECTION:
    {
        emit _setInfoPanel("Direction light\n");
        shader_path = ":/shaders/dir_light";
        success = createShaderProgram(shaderProgram,shader_path);
        if(!success){return;}
        break;
    }
    case LIGHT_POINT_GOURAND:
    {
        emit _setInfoPanel("Point light: Gourand\n");
        shader_path = ":/shaders/point_gourand";
        success = createShaderProgram(shaderProgram,shader_path);
        if(!success){return;}
        break;
    }
    case LIGHT_POINT_PHONG:
    {
        emit _setInfoPanel("Point light: Phong\n");
        shader_path = ":/shaders/point_phong";
        success = createShaderProgram(shaderProgram,shader_path);
        if(!success){return;}
        break;
    }
    case LIGHT_POINT_BLINNPHONG:
    {
        emit _setInfoPanel("Point light: Blinn-Phong\n");
        shader_path = ":/shaders/point_blinnphong";
        success = createShaderProgram(shaderProgram,shader_path);
        if(!success){return;}
        break;
    }

    }

    m_mvMatrixLoc = shaderProgram->uniformLocation("mvMatrix");
    m_projMatrixLoc = shaderProgram->uniformLocation("projMatrix");
    if(_light_Diffuse||_light_Direction||_light_Point_Gourand||_light_Point_Phong||_light_Point_BlinnPhong){
        m_normalMatrixLoc = shaderProgram->uniformLocation("normalMatrix");
        if(_light_Point_Gourand||_light_Point_Phong||_light_Point_BlinnPhong){
            m_lightAmbLoc   = shaderProgram->uniformLocation("plight.ambient");
            m_lightDiffLoc  = shaderProgram->uniformLocation("plight.diffuse");
            m_lightSpecLoc  = shaderProgram->uniformLocation("plight.specular");
            m_lightPosLoc   = shaderProgram->uniformLocation("plight.position");
        }
    }

}

void OglWidget::_setMaterial(int m){
    if(_light_Point_Gourand||_light_DFL||_light_Diffuse||_light_Direction||_light_Point_Phong||_light_Point_BlinnPhong)
    {
        shaderProgram->bind();
        shaderProgram->setUniformValue(shaderProgram->uniformLocation("hasMaterial"),has_materials);

        m_materialAmbLoc    = shaderProgram->uniformLocation("material.ambient");
        m_materialDiffLoc   = shaderProgram->uniformLocation("material.diffuse");
        m_materialSpecLoc   = shaderProgram->uniformLocation("material.specular");
        m_materialShiLoc    = shaderProgram->uniformLocation("material.shiness");

        switch(m){
        case MATERIAL_DEFAULT:
            emit _setInfoPanel("Default material\n");
            break;
        case MATERIAL_GOLD:
            emit _setInfoPanel("Gold material\n");
            shaderProgram->setUniformValue(m_materialDiffLoc,Utils::goldAmbient());
            shaderProgram->setUniformValue(m_materialDiffLoc,Utils::goldDiffuse());
            shaderProgram->setUniformValue(m_materialSpecLoc,Utils::goldSpecular());
            shaderProgram->setUniformValue(m_materialShiLoc,Utils::goldShininess());
            break;
        case MATERIAL_SILVER:
            emit _setInfoPanel("Silver material\n");
            shaderProgram->setUniformValue(m_materialDiffLoc,Utils::silverAmbient());
            shaderProgram->setUniformValue(m_materialDiffLoc,Utils::silverDiffuse());
            shaderProgram->setUniformValue(m_materialSpecLoc,Utils::silverSpecular());
            shaderProgram->setUniformValue(m_materialShiLoc,Utils::silverShininess());
            break;
        case MATERIAL_BRONZE:
            emit _setInfoPanel("Bronze material\n");
            shaderProgram->setUniformValue(m_materialDiffLoc,Utils::bronzeAmbient());
            shaderProgram->setUniformValue(m_materialDiffLoc,Utils::bronzeDiffuse());
            shaderProgram->setUniformValue(m_materialSpecLoc,Utils::bronzeSpecular());
            shaderProgram->setUniformValue(m_materialShiLoc,Utils::bronzeShininess());
            break;
        case MATERIAL_JADE:
            emit _setInfoPanel("Jade material\n");
            shaderProgram->setUniformValue(m_materialDiffLoc,Utils::jadeAmbient());
            shaderProgram->setUniformValue(m_materialDiffLoc,Utils::jadeDiffuse());
            shaderProgram->setUniformValue(m_materialSpecLoc,Utils::jadeSpecular());
            shaderProgram->setUniformValue(m_materialShiLoc,Utils::jadeShininess());
            break;
        case MATERIAL_PEARL:
            emit _setInfoPanel("Pearl material\n");
            shaderProgram->setUniformValue(m_materialDiffLoc,Utils::pearlAmbient());
            shaderProgram->setUniformValue(m_materialDiffLoc,Utils::pearlDiffuse());
            shaderProgram->setUniformValue(m_materialSpecLoc,Utils::pearlSpecular());
            shaderProgram->setUniformValue(m_materialShiLoc,Utils::pearlShininess());
            break;
        }

        shaderProgram->release();
    }else{
        emit _setInfoPanel("No materials\n");
    }
}

void OglWidget::drawScene()
{
    shaderProgram->bind();
    shaderProgram->setUniformValue(m_projMatrixLoc,pMat);
    shaderProgram->setUniformValue(m_mvMatrixLoc,mvStack.top());
    shaderProgram->setUniformValue(shaderProgram->uniformLocation("partColor"),
                                   _usePartColor);
    shaderProgram->setUniformValue(shaderProgram->uniformLocation("pureColor"),
                                   _pureSurfaceColor.redF(),_pureSurfaceColor.greenF(),_pureSurfaceColor.blueF());
    shaderProgram->setUniformValue(shaderProgram->uniformLocation("alpha"),
                                   alpha1);

    if(_light_Diffuse){
        m_invTrMat = mvStack.top().normalMatrix();
        shaderProgram->setUniformValue(m_normalMatrixLoc,m_invTrMat);
        shaderProgram->setUniformValue(shaderProgram->uniformLocation("lightDir"),1.0f,1.0f,1.0f);
    }
    else if(_light_Direction){
        m_invTrMat = mvStack.top().normalMatrix();
        shaderProgram->setUniformValue(m_normalMatrixLoc,m_invTrMat);
        shaderProgram->setUniformValue(shaderProgram->uniformLocation("lightDir"),1.0f,1.0f,1.0f);
    }
    else if(_light_Point_Gourand||_light_Point_Phong||_light_Point_BlinnPhong){

        shaderProgram->setUniformValue(m_lightAmbLoc,0.0f,0.0f,0.0f,1.0f );
        shaderProgram->setUniformValue(m_lightDiffLoc,1.0f,1.0f,1.0f,1.0f);
        shaderProgram->setUniformValue(m_lightSpecLoc,1.0f,1.0f,1.0f,1.0f );
        shaderProgram->setUniformValue(m_lightPosLoc,lightPosV);

        m_invTrMat.setToIdentity();
        m_invTrMat = mvStack.top().normalMatrix();

        shaderProgram->setUniformValue(m_normalMatrixLoc,m_invTrMat);
    }

    m_vao.bind();
    m_ebo.bind();

    if(indicesDraw){
        glEnable(GL_BLEND); //混合功能,启用透明
        glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); //设置混合函数因子
        glEnable(GL_CULL_FACE); //开启背面剔除

        glCullFace(GL_FRONT);
        glDrawElements(GL_TRIANGLES,m_indices.size(),GL_UNSIGNED_INT,0);
        glCullFace(GL_BACK);
        glDrawElements(GL_TRIANGLES,m_indices.size(),GL_UNSIGNED_INT,0);

        if(_createMyelinSheath){
            shaderProgram->setUniformValue(shaderProgram->uniformLocation("alpha"),alpha2);
            //glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); //设置混合函数因子

            glCullFace(GL_FRONT);
            glDrawElements(GL_TRIANGLES,axonIndices.size(),GL_UNSIGNED_INT,(void*)(m_indices.size()*sizeof(unsigned)));
            glCullFace(GL_BACK);
            glDrawElements(GL_TRIANGLES,axonIndices.size(),GL_UNSIGNED_INT,(void*)(m_indices.size()*sizeof(unsigned)));

            shaderProgram->setUniformValue(shaderProgram->uniformLocation("alpha"),alpha1);
        }

        glDisable(GL_CULL_FACE);
        glDisable(GL_BLEND);
    }
    else{
        glDrawArrays(GL_TRIANGLES,0,m_verts.size());
    }

    shaderProgram->release();

    if(is_Fill_Lines)
    {
       //fill the edge with solid lines
        std::unique_ptr<QOpenGLShaderProgram>lineProgram(new QOpenGLShaderProgram);
        createShaderProgram(lineProgram.get(),":/shaders/fill_line");
        lineProgram->bind();
        lineProgram->setUniformValue(lineProgram->uniformLocation("projMatrix"),pMat);
        lineProgram->setUniformValue(lineProgram->uniformLocation("mvMatrix"),mvStack.top());

        glPolygonMode(GL_FRONT_AND_BACK,GL_LINE);
        glLineWidth(1.0f);
        glEnable(GL_POLYGON_OFFSET_LINE);
        glPolygonOffset(-1.0f,1.0f);
        glDrawElements(GL_TRIANGLES,m_indices.size(),GL_UNSIGNED_INT,0);
        glDisable(GL_POLYGON_OFFSET_LINE);
        glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);

        lineProgram->release();
    }

    m_ebo.release();
    m_vao.release();
}

void OglWidget::_drawBaseObjects(){
    _drawCenter();
    //_drawGrid();
    //_drawCube();
    _drawAxes();//_drawFrameAxis();

//    glEnable(GL_SCISSOR_TEST);    //开启裁剪测试
//    glClearColor(0.3,0.3,0.3,1.0);  //裁剪区清除颜色设置为深灰色
//    glClear(GL_COLOR_BUFFER_BIT);
//    glScissor(this->width()*0.75,this->height()*0.75,this->width(),this->height());
//    _drawAxes();
//    glDisable(GL_SCISSOR_TEST);
}

//public slots
void OglWidget::setMenuMode(int render_mode)
{
    makeCurrent();              //if not, change the whole window
    switch(render_mode)
    {
    case RENDER_POINT:
        is_Fill_Lines = false;
        glPolygonMode(GL_FRONT_AND_BACK,GL_POINT);
        break;

    case RENDER_WIRE_FRAME:
        is_Fill_Lines = false;
        glPolygonMode(GL_FRONT_AND_BACK,GL_LINE);
        break;

    case RENDER_FILL:
        is_Fill_Lines = false;
        glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);
        break;

    case RENDER_FILL_LINES:
    {
        if(!is_Fill_Lines){
            glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);
        }
        is_Fill_Lines = !is_Fill_Lines;
        break;
    }

    case BASIC_INFO:
    {
        _geometry = !_geometry;
        _setLightState(false,true,false,false,false,false);
        setGLSL(LIGHT_DIFFUSE);
        break;
    }

    case CLEAR_BACKGROUND_COLOR:
    {
        this->_bgColor = QColorDialog::getColor();
        break;
    }

    case PURE_SURFACE_COLOR:
    {
        this->_pureSurfaceColor = QColorDialog::getColor();
        break;
    }

    case LIGHT_DEFAULT:
        _setLightState(true,false,false,false,false,false);
        setGLSL(LIGHT_DEFAULT);
        break;

    case LIGHT_DIFFUSE:
        _setLightState(false,true,false,false,false,false);
        setGLSL(LIGHT_DIFFUSE);
        break;

    case LIGHT_DIRECTION:
        _setLightState(false,false,true,false,false,false);
        setGLSL(LIGHT_DIRECTION);
        break;

    case LIGHT_POINT_GOURAND:
        _setLightState(false,false,false,true,false,false);
        setGLSL(LIGHT_POINT_GOURAND);
        break;

    case LIGHT_POINT_PHONG:
        _setLightState(false,false,false,false,true,false);
        setGLSL(LIGHT_POINT_PHONG);
        break;

    case LIGHT_POINT_BLINNPHONG:
        _setLightState(false,false,false,false,false,true);
        setGLSL(LIGHT_POINT_BLINNPHONG);
        break;

    case MATERIAL_DEFAULT:
        has_materials = false;
        _setMaterial(MATERIAL_DEFAULT);
        break;

    case MATERIAL_GOLD:
        has_materials = true;
        _setMaterial(MATERIAL_GOLD);
        break;

    case MATERIAL_SILVER:
        has_materials = true;
        _setMaterial(MATERIAL_SILVER);
        break;

    case MATERIAL_BRONZE:
        has_materials = true;
        _setMaterial(MATERIAL_BRONZE);
        break;

    case MATERIAL_JADE:
        has_materials = true;
        _setMaterial(MATERIAL_JADE);
        break;

    case MATERIAL_PEARL:
        has_materials = true;
        _setMaterial(MATERIAL_PEARL);
        break;

    case NEURON_DIFFERENT_COLOR:
        if(neuron){
            _usePartColor = !_usePartColor;
            if(has_materials = true){
                has_materials = false;
                _setMaterial(MATERIAL_DEFAULT);
            }
        }else{
            emit _setInfoPanel("Please open a neuron model\n");
        }
        break;

    case GROW_SPINE:
        if(neuron){
            _createSpine = !_createSpine;
            _growSpine();
        }else{
            emit _setInfoPanel("Please open a neuron model\n");
        }
        break;

    case GROW_MYELIN_SHEATH:
        if(neuron){
            _createMyelinSheath = !_createMyelinSheath;
            _growMyelinSheath();

        }else{
            emit _setInfoPanel("Please open a neuron model\n");
        }
        break;

    case ALPHA_NEURON:
    case ALPHA_AXON:
        break;  //只需update就行

    case SOMA_FIXED:
        _simulateRealSoma();
        break;

    case SOMA_AUTO:
        _simulateRealSomaAuto();
        break;

    case SMOOTH_TAUBIN:
        if(taubinDlg==nullptr){
            taubinDlg = new QSmoothDialog("Taubin Smooth",this);
            connect(taubinDlg,&QSmoothDialog::Smooth,this,&OglWidget::_smoothDendrites);
            connect(taubinDlg,&QSmoothDialog::Smooth,(Window*)this->parent(),&Window::_computeMeasurements);
        }
        taubinDlg->show();
        break;
    }
    update();
}

void OglWidget::_setInfos()
{
    _drawInfos2d = !_drawInfos2d;
    update();
}

void OglWidget::_setAlpha1(double v)
{
    alpha1 = (float)v;
    setMenuMode(ALPHA_NEURON);
}

void OglWidget::_setAlpha2(double v)
{
    alpha2 = (float)v;
    setMenuMode(ALPHA_AXON);
}

void OglWidget::_setInteractorSpeed(int val)
{
    llxInteractor.setSpeed(val);
}

void OglWidget::_setCircleOutter(int val)
{
    circleOutter = val;
}

void OglWidget::_setCircleInner(int val)
{
    circleInner = val;
}

/*
 * make object, get data
 */

void OglWidget::_loadObject(QString &str)
{
    _resetSceneState();  //加载新模型前需要做一些清理工作

    QFile file(str);
    if(!file.open(QFile::ReadOnly|QFile::Text)){
        QMessageBox::warning(this,tr("Ogl"),tr("Cannot read file %1:\n%2.").arg(QDir::toNativeSeparators(str)));
        return;
    }
    bool hasVertMarks = false;
    bool hasRoomType = false;
    std::vector<unsigned>colorMark;
    std::vector<unsigned>typeMark;
    std::vector<unsigned>frooms;

    QTextStream in(&file);
    QString line;
    QStringList list;
    QVector<QString>ls;

    Point3D point;
    while(!in.atEnd())
    {
        line=in.readLine();
        list = line.split(' ');//将字符串分割为子串，并存储为字符串列表
        ls = list.toVector(); //将字符串列表转化为vector容器

        if(ls[0]=="vmarks"){
            hasVertMarks = true;
        }
        else if(ls[0]=="fmarks"){
            hasRoomType = true;
        }
        else if(ls[0]=="skeleton"){
            for(int i = 1;i<ls.size();i++){
                skeletonInfos.push_back(ls[i]);
            }
        }
        else if(ls[0]=='v'){
            point.vert_pos.setX(ls[1].toFloat());
            point.vert_pos.setY(ls[2].toFloat());
            point.vert_pos.setZ(ls[3].toFloat());
            if(hasVertMarks){
                //设置颜色标记
                colorMark.emplace_back(ls[4].toUInt());
                switch(ls[4].toInt()){
                case 2:
                    point.vert_color = QVector3D(1.0f,0.0f,0.0f);
                    break;
                case 3:
                    point.vert_color = QVector3D(0.0f,1.0f,0.0f);
                    break;
                default:
                    point.vert_color = QVector3D(1.0f,0.5f,0.31f);  //不能省略
                    break;
                }

                typeMark.emplace_back(ls[5].toInt());//设置形变标记，用来标识边界
            }

            m_verts.push_back(point);
        }
        else if(ls[0]=='f'){
            if(hasRoomType){
                m_indices.push_back(ls[1].toUInt()-1);
                m_indices.push_back(ls[2].toUInt()-1);
                m_indices.push_back(ls[3].toUInt()-1);
                frooms.push_back(ls[4].toUInt());

            }else{
                for(int i=1;i<ls.size();i++)
                    m_indices.push_back(ls[i].toUInt()-1);
            }
        }
        else if(ls[0] == "neuron"){
            neuron = true;
            emit enableNeuronGroup(true);

        }
        else{
            continue;
        }
    }

    m_vn = m_verts.size();
    m_fn = m_indices.size();
    emit setVN(QString::number(m_vn));
    emit setFN(QString::number(m_fn/3));

    setVcgMesh(colorMark,typeMark,frooms);

    //设置顶点法线
    for(int i=0; i<m_vn; ++i){
        m_verts[i].vert_normal.setX(_vcgmesh.vert[i].N().X());
        m_verts[i].vert_normal.setY(_vcgmesh.vert[i].N().Y());
        m_verts[i].vert_normal.setZ(_vcgmesh.vert[i].N().Z());
    }

    makeCurrent();

    m_vao.bind();
    m_vbo.bind();
    m_ebo.bind();
    m_vbo.allocate(&m_verts[0],m_verts.size()*sizeof(Point3D));
    m_ebo.allocate(&m_indices[0],m_indices.size()*sizeof(unsigned int));

    setupVertexAttribs();
    m_ebo.release();
    m_vbo.release();
    m_vao.release();

    update();

    emit _setInfoPanel(str+"\n");
}

void OglWidget::setDefaultObject()
{
    if(!m_verts.empty()){
        m_verts.clear();
        m_indices.clear();
        _vcgmesh.Clear();
    }

    Shapes myTours(0.5f,0.2f,48);
    //Shapes myTours(48);
    QVector<QVector3D>vert = myTours.getVertices();
    QVector<QVector2D>tex = myTours.getTexCoords();
    QVector<QVector3D>norm = myTours.getNormals();

    int numVertices = myTours.getNumVertices();

    for(int i=0;i<numVertices;++i){
        Point3D curPoint;
        curPoint.vert_pos = vert[i];
        curPoint.vert_normal = norm[i];
        m_verts.push_back(curPoint);
    }

    m_indices = myTours.getIndices();

    m_vn = m_verts.size();
    m_fn = m_indices.size();
    emit setVN(QString::number(m_vn));
    emit setFN(QString::number(m_fn/3));

    std::vector<unsigned>type;
    setVcgMesh(type,type,type);

    m_vao.bind();
    m_vbo.bind();
    m_ebo.bind();

    m_vbo.allocate(&m_verts[0],m_verts.size()*sizeof(Point3D));
    m_ebo.allocate(&m_indices[0],m_indices.size()*sizeof(unsigned));

    setupVertexAttribs();

    m_vbo.release();
    m_ebo.release();
    m_vao.release();

    emit _setInfoPanel("Default model\n");
}

void OglWidget::setupVertexAttribs()
{
    QOpenGLFunctions *f = QOpenGLContext::currentContext()->functions();
    f->glEnableVertexAttribArray(0);
    f->glEnableVertexAttribArray(1);
    f->glEnableVertexAttribArray(2);
    f->glVertexAttribPointer(0,3,GL_FLOAT,GL_FALSE,sizeof(Point3D),0);
    f->glVertexAttribPointer(1,3,GL_FLOAT,GL_FALSE,sizeof(Point3D),reinterpret_cast<void*>(sizeof(QVector3D)));
    f->glVertexAttribPointer(2,3,GL_FLOAT,GL_FALSE,sizeof(Point3D),reinterpret_cast<void*>(2*sizeof(QVector3D)));
}

void OglWidget::setVcgMesh(std::vector<unsigned>&vtype1,std::vector<unsigned>&vtype2,std::vector<unsigned>&ftype)
{
    //vtype1是顶点颜色标记，对应room_type，vtype2是顶点是否移动，对应markVert，vtype3是面片所属结构域标记
    if(vtype1.empty()&&vtype2.empty()){
        for(int i=0;i<m_vn;i++){
            _vcgmesh.addOneVertex(vcg::Point3f(m_verts[i].vert_pos.x(),m_verts[i].vert_pos.y(),m_verts[i].vert_pos.z()),i);
        }
    }
    else{
        for(int i=0;i<m_vn;i++){
             _vcgmesh.addOneVertex(vcg::Point3f(m_verts[i].vert_pos.x(),m_verts[i].vert_pos.y(),m_verts[i].vert_pos.z()),i,vtype1[i],vtype2[i]);
        }
    }

    if(ftype.empty()){
        for(int i = 0, j=0;i<m_fn;i+=3,j++){
            _vcgmesh.addOneFace(m_indices[i],m_indices[i+1],m_indices[i+2],j);
        }
    }
    else{
        for(int i = 0, j=0;i<m_fn;i+=3,j++){
            _vcgmesh.addOneFace(m_indices[i],m_indices[i+1],m_indices[i+2],j,ftype[j]);
        }
    }

    int dupv = vcg::tri::Clean<vcgMesh>::RemoveDuplicateVertex(_vcgmesh);
    int urefv = vcg::tri::Clean<vcgMesh>::RemoveUnreferencedVertex(_vcgmesh);
    if(dupv!=0 ||urefv!=0){
        qDebug()<<"dupv = "<<dupv<<"\turefv = "<<urefv;
        vcg::tri::Allocator<vcgMesh>::CompactVertexVector(_vcgmesh);
    }

    //计算法线
    vcg::tri::UpdateNormal<vcgMesh>::PerVertexClear(_vcgmesh);
    vcg::tri::UpdateNormal<vcgMesh>::PerVertexNormalized(_vcgmesh);
}

void OglWidget::_growSpine()
{
    makeCurrent();
    //prepare data
    if(_spinemesh==nullptr){
        setSpineMesh();
    }

    int offsetOld = 0;
    int offsetNew = 0;

    vcgMesh *tmp = nullptr;
    if(_createSpine==true){
        tmp = _spinemesh;    
        offsetOld = _vcgmesh.VN();
        offsetNew = _spinemesh->VN();
    }else{
        tmp = &_vcgmesh;
        offsetOld = _spinemesh->VN();
        offsetNew = _vcgmesh.VN();
    }

    if(!m_verts.empty()){
        m_verts.clear();
        m_indices.clear();
    }

    m_vn = tmp->VN();
    m_fn = tmp->FN();
    emit setVN(QString::number(m_vn));
    emit setFN(QString::number(m_fn));

    m_verts.reserve(m_vn);
    m_indices.reserve(m_fn*3);
    Point3D point;

    for(auto &v:tmp->vert){
        point.vert_pos = QVector3D(v.P().X(),v.P().Y(),v.P().Z());
        point.vert_normal = QVector3D(v.N().X(),v.N().Y(),v.N().Z());

        switch(v.roomType){
        case 2:
            point.vert_color = QVector3D(1.0f,0.0f,0.0f);
            break;
        case 3:
            point.vert_color = QVector3D(0.0f,1.0f,0.0f);
            break;
        default:
            point.vert_color = QVector3D(1.0f,0.5f,0.31f);
            break;
        }
        m_verts.push_back(point);
    }

    for(auto &f:tmp->face){
        m_indices.push_back(f.V(0)->id);
        m_indices.push_back(f.V(1)->id);
        m_indices.push_back(f.V(2)->id);
    }

    m_vao.bind();
    m_vbo.bind();
    m_ebo.bind();

    if(_createMyelinSheath==true){
        for(auto &val:axonIndices){
            val -= offsetOld;
            val += offsetNew;
        }
        int count1 = m_verts.size()*sizeof(Point3D);
        int count2 = m_indices.size()*sizeof(unsigned);

        int count3 = axonVerts.size()*sizeof(Point3D);
        int count4 = axonIndices.size()*sizeof(unsigned);

        int countVBO = count1 + count3;
        int countEBO = count2 + count4;

        m_vbo.allocate(countVBO);
        m_vbo.write(0,&m_verts[0],count1);
        m_vbo.write(count1,&axonVerts[0],count3);

        m_ebo.allocate(countEBO);
        m_ebo.write(0,&m_indices[0],count2);
        m_ebo.write(count2,&axonIndices[0],count4);

        setupVertexAttribs();
    }
    else{
        m_vbo.allocate(&m_verts[0],m_verts.size()*sizeof(Point3D));
        m_ebo.allocate(&m_indices[0],m_indices.size()*sizeof(unsigned));
        setupVertexAttribs();
    }


    m_vbo.release();
    m_ebo.release();
    m_vao.release();
}

void OglWidget::setSpineMesh()
{
    _spinemesh = new vcgMesh();
    vcg::tri::Append<vcgMesh,vcgMesh>::MeshCopy(*_spinemesh,_vcgmesh);

    _spinemesh->setVFAttributes(&_vcgmesh);

    int vid = _spinemesh->VN();
    int fid = _spinemesh->FN();
    vcg::tri::UpdateTopology<vcgMesh>::VertexFace(*_spinemesh);
    vcg::tri::UpdateTopology<vcgMesh>::FaceFace(*_spinemesh);
    vcg::tri::UpdateNormal<vcgMesh>::PerVertexNormalized(*_spinemesh);
    vcg::tri::UpdateNormal<vcgMesh>::PerFaceNormalized(*_spinemesh);

    int oldFN = _spinemesh->FN();

    std::default_random_engine e(time(0));
    std::uniform_int_distribution<unsigned>u(40,60);

    for(int idx = 0;idx<oldFN;idx+=u(e)){

        vcgFace *f = &_spinemesh->face[idx];

        if(f->roomType!=3)
            continue;

        std::vector<int>vtmp;
        std::vector<int>ftmp;
        bool hasD = false;

        //当前处理的面片，找到其第一个顶点的一阶邻域面片
        vcg::face::JumpingPos<vcgFace>p(f,0,f->V(0));
        do{
            if(!p.f->IsD())
                ftmp.push_back(p.f->id);
            else{
                hasD = true;
                break;
            }
            p.NextFE();
        }while(p.f!=f);

        if(hasD){
            continue;
        }

        for(auto ft:ftmp){
            auto iter = vcg::tri::Allocator<vcgMesh>::AddVertex(*_spinemesh,vcg::Barycenter(_spinemesh->face[ft]));
            iter->id = vid++;
            vtmp.push_back(iter->id);
        }

        //添加相应的面片，用f2容器保存编号
        vcgMesh::FaceIterator fi = vcg::tri::Allocator<vcgMesh>::AddFaces(*_spinemesh,vtmp.size());
        f = &_spinemesh->face[idx];
        std::vector<int>f2;
        for(size_t i=0;i<vtmp.size();i++){
            fi->V(0) = &(_spinemesh->vert[vtmp[i]]);
            if(_spinemesh->face[ftmp[i]].V(0)==f->V(0)){
                fi->V(1) = _spinemesh->face[ftmp[i]].V(1);
                fi->V(2) = _spinemesh->face[ftmp[i]].V(2);
            }else if(_spinemesh->face[ftmp[i]].V(1)==f->V(0)){
                fi->V(1) = _spinemesh->face[ftmp[i]].V(2);
                fi->V(2) = _spinemesh->face[ftmp[i]].V(0);
            }else if(_spinemesh->face[ftmp[i]].V(2)==f->V(0)){
                fi->V(1) = _spinemesh->face[ftmp[i]].V(0);
                fi->V(2) = _spinemesh->face[ftmp[i]].V(1);
            }
            fi->id = fid++;
            f2.push_back(fi->id);
            ++fi;
        }

        //再次添加修补面片
        vcgMesh::FaceIterator fj = vcg::tri::Allocator<vcgMesh>::AddFaces(*_spinemesh,2*f2.size());
        f = &_spinemesh->face[idx];
        int count = (int)f2.size();
        for(int i=0;i<count;i++){
            int mdx = i%count;
            int ndx = (i+1)%count;

            fj->V(0) = _spinemesh->face[f2[mdx]].V(0);
            fj->V(1) = _spinemesh->face[f2[ndx]].V(0);
            fj->V(2) = _spinemesh->face[f2[ndx]].V(2);
            fj->id = fid++;
            ++fj;

            fj->V(0) = f->V(0);
            fj->V(1) = _spinemesh->face[f2[ndx]].V(0);
            fj->V(2) = _spinemesh->face[f2[mdx]].V(0);
            fj->id = fid++;
            ++fj;
        }

        //更新顶点坐标
        for(size_t i=0;i<vtmp.size();i++){
            float len = _spinemesh->face[ftmp[i]].computeEdgeLength();
            vcg::Point3f npos;
            npos.X() = _spinemesh->vert[vtmp[i]].P().X()+len*_spinemesh->face[ftmp[i]].N().X();
            npos.Y() = _spinemesh->vert[vtmp[i]].P().Y()+len*_spinemesh->face[ftmp[i]].N().Y();
            npos.Z() = _spinemesh->vert[vtmp[i]].P().Z()+len*_spinemesh->face[ftmp[i]].N().Z();
            _spinemesh->vert[vtmp[i]].setPos(npos);
        }

        //更新中心顶点坐标f->V(0)
        f = &_spinemesh->face[idx];
        vcg::Point3f npos;
        float len = f->computeEdgeLength();
        npos.X() = f->P(0).X()+len*f->V(0)->N().X();
        npos.Y() = f->P(0).Y()+len*f->V(0)->N().Y();
        npos.Z() = f->P(0).Z()+len*f->V(0)->N().Z();
        f->setPos(0,npos);

        //移除多余的面片
         for(auto fd:ftmp){
             vcg::tri::Allocator<vcgMesh>::DeleteFace(*_spinemesh,_spinemesh->face[fd]);
         }
   }

    vcg::tri::Allocator<vcgMesh>::CompactFaceVector(*_spinemesh);   //删除多余的面片

    int dupv = vcg::tri::Clean<vcgMesh>::RemoveDuplicateVertex(*_spinemesh);
    int dupr = vcg::tri::Clean<vcgMesh>::RemoveUnreferencedVertex(*_spinemesh);

    if(dupr!=0||dupv!=0){
        qDebug()<<"duplicate = "<<dupv<<"; unref = "<<dupr;
        vcg::tri::Allocator<vcgMesh>::CompactVertexVector(*_spinemesh);
    }

    vcg::tri::UpdateNormal<vcgMesh>::PerVertexClear(*_spinemesh);
    vcg::tri::UpdateNormal<vcgMesh>::PerVertexNormalized(*_spinemesh);
}

void OglWidget::_growMyelinSheath()
{
    makeCurrent();

    if(_axonmesh==nullptr){
        setMyelinSheathMesh();
    }

    static bool onceFlag = false;
    int offset = (_createSpine==true)?_spinemesh->VN():_vcgmesh.VN();

    if(onceFlag == true){
        offset = -offset;
        onceFlag = false;
    }

    for(auto &val:axonIndices){
        val += offset;
    }

    m_vao.bind();
    m_vbo.bind();
    m_ebo.bind();

    if(_createMyelinSheath==true){
        int count1 = m_verts.size()*sizeof(Point3D);
        int count2 = m_indices.size()*sizeof(unsigned);

        int count3 = axonVerts.size()*sizeof(Point3D);
        int count4 = axonIndices.size()*sizeof(unsigned);

        int countVBO = count1 + count3;
        int countEBO = count2 + count4;

        m_vbo.allocate(countVBO);
        m_vbo.write(0,&m_verts[0],count1);
        m_vbo.write(count1,&axonVerts[0],count3);

        m_ebo.allocate(countEBO);
        m_ebo.write(0,&m_indices[0],count2);
        m_ebo.write(count2,&axonIndices[0],count4);

        setupVertexAttribs();
        onceFlag = true;
    }
    else{
        m_vbo.allocate(&m_verts[0],m_verts.size()*sizeof(Point3D));
        m_ebo.allocate(&m_indices[0],m_indices.size()*sizeof(unsigned));
        setupVertexAttribs();
    }

    m_vbo.release();
    m_ebo.release();
    m_vao.release();
}

void OglWidget::setMyelinSheathMesh()
{
    _axonmesh = new vcgMesh();

    //遍历原始网格，找出轴突表面
    vcgMesh::FaceIterator fcur = _vcgmesh.face.begin();
    while(fcur!=_vcgmesh.face.end()){
        if(fcur->roomType==2){
            vcgMesh::VertexIterator viter = vcg::tri::Allocator<vcgMesh>::AddVertices(*_axonmesh,3);
            vcgMesh::FaceIterator fiter = vcg::tri::Allocator<vcgMesh>::AddFaces(*_axonmesh,1);
            viter->P() = fcur->P(0);
            fiter->V(0) = &(*viter);

            ++viter;
            viter->P() = fcur->P(1);
            fiter->V(1) = &(*viter);

            ++viter;
            viter->P() = fcur->P(2);
            fiter->V(2) = &(*viter);
        }

        ++fcur;
    }

    int dupv = vcg::tri::Clean<vcgMesh>::RemoveDuplicateVertex(*_axonmesh);
    int dupr = vcg::tri::Clean<vcgMesh>::RemoveUnreferencedVertex(*_axonmesh);
    if(dupv!=0||dupr!=0){
        //qDebug()<<"duplicate = "<<dupv<<"; unref = "<<dupr;
        vcg::tri::Allocator<vcgMesh>::CompactVertexVector(*_axonmesh);
    }

    vcg::tri::UpdateNormal<vcgMesh>::PerVertexClear(*_axonmesh);
    vcg::tri::UpdateNormal<vcgMesh>::PerVertexNormalized(*_axonmesh);

    //将每个顶点沿着沿着法线外移，最终构成髓鞘结构
    int vid = 0;
    for(auto &v:_axonmesh->vert){
        v.id = vid++;
        v.P() = v.P() + 0.2f*v.N();
    }

    //保存数据
    Point3D point;
    for(auto &v:_axonmesh->vert){
        point.vert_pos = QVector3D(v.P().X(),v.P().Y(),v.P().Z());
        point.vert_normal = QVector3D(v.N().X(),v.N().Y(),v.N().Z());
        point.vert_color = QVector3D(0.9f,0.9f,0.9f);
        axonVerts.push_back(point);
    }

    for(auto &f:_axonmesh->face){
        axonIndices.push_back(f.V(0)->id);
        axonIndices.push_back(f.V(1)->id);
        axonIndices.push_back(f.V(2)->id);
    }
}

void OglWidget::_simulateRealSoma()
{
    if(_somamesh == nullptr){
        _somamesh = new vcgMesh();

        vcg::tri::Append<vcgMesh,vcgMesh>::MeshCopy(*_somamesh,_vcgmesh,true);

        _somamesh->resetSelectedMark();
        _somamesh->setVertID();
        vcg::tri::UpdateTopology<vcgMesh>::VertexFace(*_somamesh);
    }

    //形变模型实现，借助FairSurface的接口
    FairSurface fm;
    fm._vm = _somamesh;
    std::set<int>s2;    //一次形变过程中的形变中心顶点，初始为映射点
    std::set<int>nextCenters;   //下一次形变过程中的中心顶点
    std::set<int>dels;

    int circleNum = circleOutter;
    int affectOne = circleInner;
    int affectMax = 15;

    while(circleNum--){
        dels.clear();
        s2.clear();
        nextCenters.clear();

        for(auto &v:_somamesh->vert){
            if (_somamesh->markVert[v] == 1) {
                dels.insert(v.id);	//前几层形变可能会将轮廓顶点作为一阶邻域,因此始终判断一下
            }
            if (_somamesh->markVert[v] == 2) {
                s2.insert(v.id);
            }
        }
        for (int i = 0; i < affectOne; i++)
        {
            _somamesh->updateVertex();
            for (auto s : s2) {
                fm.Taubin(&_somamesh->vert[s], 0.6307f);
                nextCenters.insert(_somamesh->vert[s].neighbors.begin(), _somamesh->vert[s].neighbors.end());
                dels.insert(s);
            }
            _somamesh->updateVertex();
            for (auto s : s2) {
                fm.Taubin(&_somamesh->vert[s], -0.673156f);
            }
            for (auto s : dels) {
                auto iter = nextCenters.find(s);
                if (iter != nextCenters.end()) {
                    nextCenters.erase(iter);
                }
            }
            s2 = nextCenters;
            nextCenters.clear();
        }

        if (affectOne <= affectMax) {
            ++affectOne;
        }
    }

    //数据映射回_vcgmesh中，并加载到OpenGL
    for(auto &v:_somamesh->vert){
        for(auto &ov:_vcgmesh.vert){
            if(ov.id==_somamesh->markID[v]){
                ov.P()=v.P();
                break;
            }
        }
        m_verts[_somamesh->markID[v]].vert_pos = QVector3D(v.P().X(),v.P().Y(),v.P().Z());
    }

    makeCurrent();
    m_vao.bind();
    m_vbo.bind();

    m_vbo.allocate(&m_verts[0],m_verts.size()*sizeof(Point3D));

    setupVertexAttribs();

    m_vbo.release();
    m_vao.release();

    if(_spinemesh!=nullptr){
        _spinemesh->Clear();
        delete _spinemesh;
        _spinemesh = nullptr;
    }

    return;
    int loadMask =0;
    vcg::tri::io::ExporterOBJ<vcgMesh>::Save(*_somamesh,"test.obj",loadMask);
}

void OglWidget::_simulateRealSomaAuto()
{
    for(int i=1;i<5;i++){
        _simulateRealSoma();
    }

}

void OglWidget::_smoothDendrites()
{
    if(_somamesh==nullptr){
        for(auto &f:_vcgmesh.face){
            if(f.IsS()){
                f.V(0)->SetS();
                f.V(1)->SetS();
                f.V(2)->SetS();
            }
        }
    }
    FairSurface fm;
    fm._vm = &_vcgmesh;
    fm.setLambda(taubinDlg->getLambda());
    fm.setMu();fm.setMu(taubinDlg->getMu());
    fm.nonShrinking(taubinDlg->getSteps(),neuron);

    //将新顶点位置到m_verts中,并加载到OpenGL
    for(auto &v:_vcgmesh.vert){
        m_verts[v.id].vert_pos = QVector3D(v.P().X(),v.P().Y(),v.P().Z());
    }

    makeCurrent();
    m_vao.bind();
    m_vbo.bind();

    m_vbo.allocate(&m_verts[0],m_verts.size()*sizeof(Point3D));

    setupVertexAttribs();

    m_vbo.release();
    m_vao.release();
    update();

    if(_spinemesh!=nullptr){
        _spinemesh->Clear();
        delete _spinemesh;
        _spinemesh = nullptr;
    }

    if(_axonmesh!=nullptr){
        _axonmesh->Clear();
        delete _axonmesh;
        _axonmesh = nullptr;
    }
    return;
    int loadMask =0;
    vcg::tri::io::ExporterOBJ<vcgMesh>::Save(_vcgmesh,"test.obj",loadMask);
}

bool OglWidget::createShaderProgram(QOpenGLShaderProgram *program ,QString path,bool hasGeometry)
{
    bool success = false;
    success = program->addShaderFromSourceFile(QOpenGLShader::Vertex,path + ".vert");
    if(!success)
    {
        qDebug()<<"add vertex shader failed!"<<program->log();
        return success;
    }

    if(hasGeometry){
        success = program->addShaderFromSourceFile(QOpenGLShader::Geometry,path+".txt");
        if(!success){
            qDebug()<<"add geometry shader failed!"<<program->log();
            return success;
        }
    }

    success = program->addShaderFromSourceFile(QOpenGLShader::Fragment,path + ".frag");

    if(!success)
    {
        qDebug()<<"add fragment shader failed!"<<program->log();
        return success;
    }

    success=program->link();  //链接着色器
    if(!success)
    {
        qDebug()<<"link shaderProgram failed!"<<program->log();
        return success;
    }
    return success;
}

void OglWidget::_setLightState(bool lightNo, bool lightDiff, bool lightDir, bool lightPGourand, bool lightPPhong, bool lightPBlinnPhong){
    _light_DFL=lightNo;
    _light_Diffuse=lightDiff;
    _light_Direction=lightDir;
    _light_Point_Gourand=lightPGourand;
    _light_Point_Phong=lightPPhong;
    _light_Point_BlinnPhong=lightPBlinnPhong;
}

void OglWidget::_resetSceneState()
{
    if(!m_verts.empty()){
        m_verts.clear();
        m_indices.clear();
        _vcgmesh.Clear();
    }

    if(_spinemesh!=nullptr){
        _spinemesh->Clear();
        delete _spinemesh;
        _spinemesh = nullptr;
    }

    if(_axonmesh!=nullptr){
        _axonmesh->Clear();
        delete _axonmesh;
        _axonmesh = nullptr;

        axonVerts.clear();
        axonIndices.clear();
    }

    if(_somamesh!=nullptr){
        _somamesh->Clear();
        delete _somamesh;
        _somamesh = nullptr;
    }

    if(has_materials){
        has_materials = false;
        _setMaterial(MATERIAL_DEFAULT);
    }
    if(!_light_DFL){
        _setLightState(true,false,false,false,false,false);
        setGLSL(LIGHT_DEFAULT);
    }

    neuron = false;
}

void OglWidget::_setBaseVertices(){
    //参考网格数据：GL_LINES,grids
    float p,d = 0.1f,mn = -1.0f,mx = 1.0f, eps = -1e-4f;
    int n = 20;
    for(int i =0;i<=n;++i){
        p = mn + i*d;
        grids.push_back(glm::vec3(p,mn,eps));
        grids.push_back(glm::vec3(p,mx,eps));

        grids.push_back(glm::vec3(mn,p,eps));
        grids.push_back(glm::vec3(mx,p,eps));
    }

    gridsVBO.create();
    gridsVBO.bind();
    gridsVBO.allocate(&grids[0],(int)grids.size()*sizeof(glm::vec3));
    gridsVBO.release();

    //立方体数据：GL_TRIANGLES
    cubes = {
        glm::vec3(0.2f, 0.f, 0.f),glm::vec3(0.8f, 0.4f, 0.4f),
        glm::vec3(0.2f, 0.2f, 0.f),glm::vec3(0.8f, 0.4f, 0.4f),
        glm::vec3(0.2f, 0.2f, 0.2f),glm::vec3(0.8f, 0.4f, 0.4f),
        glm::vec3(0.2f, 0.f, 0.f),glm::vec3(0.8f, 0.4f, 0.4f),
        glm::vec3(0.2f, 0.f, 0.2f),glm::vec3(0.8f, 0.4f, 0.4f),
        glm::vec3(0.2f, 0.2f, 0.2f),glm::vec3(0.8f, 0.4f, 0.4f),

        glm::vec3(0.f, 0.f, 0.f),glm::vec3(.4f, 0.2f, 0.2f),
        glm::vec3(0.f, 0.2f, 0.f),glm::vec3(.4f, 0.2f, 0.2f),
        glm::vec3(0.f, 0.2f, 0.2f),glm::vec3(.4f, 0.2f, 0.2f),
        glm::vec3(0.f, 0.f, 0.f),glm::vec3(.4f, 0.2f, 0.2f),
        glm::vec3(0.f, 0.f, 0.2f),glm::vec3(.4f, 0.2f, 0.2f),
        glm::vec3(0.f, 0.2f, 0.2f),glm::vec3(.4f, 0.2f, 0.2f),

        glm::vec3(0.f, 0.2f, 0.f),glm::vec3(0.4f, 0.8f, 0.4f),
        glm::vec3(0.2f, 0.2f, 0.f),glm::vec3(0.4f, 0.8f, 0.4f),
        glm::vec3(0.2f, 0.2f, 0.2f),glm::vec3(0.4f, 0.8f, 0.4f),
        glm::vec3(0.f, 0.2f, 0.f),glm::vec3(0.4f, 0.8f, 0.4f),
        glm::vec3(0.f, 0.2f, 0.2f),glm::vec3(0.4f, 0.8f, 0.4f),
        glm::vec3(0.2f, 0.2f, 0.2f),glm::vec3(0.4f, 0.8f, 0.4f),

        glm::vec3(0.f, 0.f, 0.f),glm::vec3(0.2f, 0.4f, 0.2f),
        glm::vec3(0.2f, 0.f, 0.f),glm::vec3(0.2f, 0.4f, 0.2f),
        glm::vec3(0.2f, 0.f, 0.2f),glm::vec3(0.2f, 0.4f, 0.2f),
        glm::vec3(0.f, 0.f, 0.f),glm::vec3(0.2f, 0.4f, 0.2f),
        glm::vec3(0.f, 0.f, 0.2f),glm::vec3(0.2f, 0.4f, 0.2f),
        glm::vec3(0.2f, 0.f, 0.2f),glm::vec3(0.2f, 0.4f, 0.2f),

        glm::vec3(0.f, 0.f, 0.2f),glm::vec3(.4f, .4f, .8f),
        glm::vec3(0.f, 0.2f, 0.2f),glm::vec3(.4f, .4f, .8f),
        glm::vec3(0.2f, 0.2f, 0.2f),glm::vec3(.4f, .4f, .8f),
        glm::vec3(0.f, 0.f, 0.2f),glm::vec3(.4f, .4f, .8f),
        glm::vec3(0.2f, 0.f, 0.2f),glm::vec3(.4f, .4f, .8f),
        glm::vec3(0.2f, 0.2f, 0.2f),glm::vec3(.4f, .4f, .8f),

        glm::vec3(0.f, 0.f, 0.f),glm::vec3(0.2f, 0.2f, .4f),
        glm::vec3(0.f, 0.2f, 0.f),glm::vec3(0.2f, 0.2f, .4f),
        glm::vec3(0.2f, 0.2f, 0.f),glm::vec3(0.2f, 0.2f, .4f),
        glm::vec3(0.f, 0.f, 0.f),glm::vec3(0.2f, 0.2f, .4f),
        glm::vec3(0.2f, 0.f, 0.f),glm::vec3(0.2f, 0.2f, .4f),
        glm::vec3(0.2f, 0.2f, 0.f),glm::vec3(0.2f, 0.2f, .4f),
    };
    cubeVBO.create();
    cubeVBO.bind();
    cubeVBO.allocate(&cubes[0],(int)cubes.size()*sizeof(glm::vec3));
    cubeVBO.release();

    //正交轴数据：GL_LINES,
    axes = {
        glm::vec3(0.0f),glm::vec3(0.5f,0.0f,0.0f),
        glm::vec3(0.0f),glm::vec3(0.0f,0.5f,0.0f),
        glm::vec3(0.0f),glm::vec3(0.0f,0.0f,0.5f)
    };
    axesVBO.create();
    axesVBO.bind();
    axesVBO.allocate(&axes[0],(int)axes.size()*sizeof(glm::vec3));
    axesVBO.release();
}

void OglWidget::_setBaseShaderProgram(){
    if(gridsProgram!=nullptr){
        delete gridsProgram;
    }
    gridsProgram = new QOpenGLShaderProgram;
    QString local_path = ":/trackball/grids";
    createShaderProgram(gridsProgram,local_path);

    if(cameraProgram!=nullptr){
        delete cameraProgram;
    }
    cameraProgram = new QOpenGLShaderProgram;
    local_path = ":/trackball/camera";
    createShaderProgram(cameraProgram,local_path);

    if(cubeProgram!=nullptr){
        delete cubeProgram;
    }
    cubeProgram = new QOpenGLShaderProgram;
    local_path = ":/trackball/cube";
    createShaderProgram(cubeProgram,local_path);

    if(axesProgram!=nullptr){
        delete axesProgram;
    }
    axesProgram=new QOpenGLShaderProgram;
    local_path = ":/trackball/axes";
    createShaderProgram(axesProgram,local_path);
}

void OglWidget::_drawCenter(){
    cameraProgram->bind();
    mvLoc = cameraProgram->uniformLocation("mvMatrix");
    projLoc = cameraProgram->uniformLocation("projMatrix");
    int posLoc = cameraProgram->uniformLocation("cameraPos");
    cameraProgram->setUniformValue(projLoc,pMat);
    cameraProgram->setUniformValue(mvLoc,mvStack.top());
    glUniform3f(posLoc,llxCamera.getCenter().x,llxCamera.getCenter().y,llxCamera.getCenter().z);

    glPointSize(15.0f);
    glDrawArrays(GL_POINTS,0,1);
    glPointSize(1.0f);
    cameraProgram->release();
}

void OglWidget::_drawGrid(){
    gridsProgram->bind();
    mvLoc = gridsProgram->uniformLocation("mvMatrix");
    projLoc = gridsProgram->uniformLocation("projMatrix");
    gridsProgram->setUniformValue(projLoc,pMat);
    gridsProgram->setUniformValue(mvLoc,mvStack.top());
    gridsVBO.bind();
    glLineWidth(2.0f);

    glVertexAttribPointer(0,3,GL_FLOAT,GL_FALSE,sizeof(glm::vec3),0);
    glEnableVertexAttribArray(0);
    glDrawArrays(GL_LINES,0,(GLsizei)grids.size());

    glLineWidth(1.0f);
    gridsVBO.release();
    gridsProgram->release();
}

void OglWidget::_drawCube(){
    cubeProgram->bind();
    mvLoc = cubeProgram->uniformLocation("mvMatrix");
    projLoc = cubeProgram->uniformLocation("projMatrix");
    cubeProgram->setUniformValue(projLoc,pMat);
    cubeProgram->setUniformValue(mvLoc,mvStack.top());

    cubeVBO.bind();
    glVertexAttribPointer(0,3,GL_FLOAT,GL_FALSE,2*sizeof(glm::vec3),(void*)0);
    glVertexAttribPointer(1,3,GL_FLOAT,GL_FALSE,2*sizeof(glm::vec3),(void*)(sizeof(glm::vec3)));
    glEnableVertexAttribArray(0);
    glEnableVertexAttribArray(1);
    glDrawArrays(GL_TRIANGLES,0,(GLsizei)cubes.size());
    cubeVBO.release();

    cubeProgram->release();
}

void OglWidget::_drawAxes(){
    axesProgram->bind();
    mvLoc = axesProgram->uniformLocation("mvMatrix");
    projLoc = axesProgram->uniformLocation("projMatrix");
    axesProgram->setUniformValue(projLoc,pMat);
    axesProgram->setUniformValue(mvLoc,mvStack.top());

    axesVBO.bind();

    glLineWidth(2.0f);
    glPointSize(2.0f);

    glVertexAttribPointer(0,3,GL_FLOAT,GL_FALSE,0,0);
    glEnableVertexAttribArray(0);

    glDrawArrays(GL_LINES,0,(GLsizei)axes.size());

    glLineWidth(1.0f);
    glPointSize(1.0f);

    axesVBO.release();
    axesProgram->release();

}

void OglWidget::_drawFrameAxis(){
    glBegin(GL_LINE_LOOP);
    mvStack.push(mvStack.top());
    mvStack.top().scale(0.05f);
    for(auto vertex:_axesLines){
        const auto translated = pMat*mvStack.top()*vertex.first;
        glColor3f(vertex.second.red(),vertex.second.green(),vertex.second.blue());
        glVertex3f(translated.x(), translated.y(), translated.z());
    }
    glEnd();
    mvStack.pop();
}

/*
 * qwidget event: virtual functions
 */

void OglWidget::mousePressEvent(QMouseEvent *event)
{
    switch (event->button()) {
    case Qt::LeftButton:
        llxInteractor.setLeftClicked(true);
        break;

    case Qt::MiddleButton:
        llxInteractor.setMiddleClicked(true);
        break;

    case Qt::RightButton:
        llxInteractor.setRightClicked(true);
        break;

    default:
        break;
    }

    llxInteractor.setClickPoint(event->pos().x(),event->pos().y());
    update();
}

void OglWidget::mouseReleaseEvent(QMouseEvent *event){
    switch (event->button()) {
    case Qt::LeftButton:
        llxInteractor.setLeftClicked(false);
        break;

    case Qt::MiddleButton:
        llxInteractor.setMiddleClicked(false);
        break;

    case Qt::RightButton:
        llxInteractor.setRightClicked(false);
        break;

    default:
        break;
    }
    update();
}

void OglWidget::mouseMoveEvent(QMouseEvent *event)
{
    llxInteractor.setClickPoint(event->pos().x(),event->pos().y());
    update();
}

void OglWidget::mouseDoubleClickEvent(QMouseEvent *event){

}

void OglWidget::keyPressEvent(QKeyEvent *event)
{
    //0928
    float length;
    switch (event->key()) {
    case Qt::Key_Escape:
        break;

    case Qt::Key_Control://GLFW_KEY_LEFT_CONTROL&GLFW_KEY_RIGHT_CONTROL
        llxInteractor.setSpeed(5.0f);
        break;

    case Qt::Key_Shift:
        llxInteractor.setSpeed(0.1f);
        break;

    case Qt::Key_F1:
    case Qt::Key_1:
        llxAnimator.setAnimation(llx::Animator::ORBIT);
        break;

    case Qt::Key_C:
        qDebug()<<"("<<llxCamera.getEye().x<<" "<<llxCamera.getEye().y<<" "<<llxCamera.getEye().z<<")";
        qDebug()<<"("<<llxCamera.getCenter().x<<" "<<llxCamera.getCenter().y<<" "<<llxCamera.getCenter().z<<")";
        qDebug()<<"("<<llxCamera.getUp().x<<" "<<llxCamera.getUp().y<<" "<<llxCamera.getUp().z<<")";
        break;

    case Qt::Key_R:
        llxCamera.reset();
        llxInteractor.setCamera(&llxCamera);
        break;

    case Qt::Key_T: //toggle motion type
        if(llxInteractor.getMotionRightClick()==llx::TrackBallInteractor::FIRSTPERSON){
            llxInteractor.setMotionRightClick(llx::TrackBallInteractor::PAN);
        }else{
            llxInteractor.setMotionRightClick(llx::TrackBallInteractor::FIRSTPERSON);
        }
        break;

    case Qt::Key_X: //snap view to axis
        length = glm::length(llxCamera.getEye()-llxCamera.getCenter());
        llxCamera.setEye(length,0.0f,0.0f);
        llxCamera.setUp(0.0f,1.0f,0.0f);
        llxCamera.cupdate();
        llxInteractor.setCamera(&llxCamera);
        break;

    case Qt::Key_Y:
        length = glm::length(llxCamera.getEye()-llxCamera.getCenter());
        llxCamera.setEye(0.0f,length,0.0f);
        llxCamera.setUp(1.0f,0.0f,0.0f);
        llxCamera.cupdate();
        llxInteractor.setCamera(&llxCamera);
        break;

    case Qt::Key_Z:
        length = glm::length(llxCamera.getEye()-llxCamera.getCenter());
        llxCamera.setEye(0.0f,0.0f,length);
        llxCamera.setUp(1.0f,0.0f,0.0f);
        llxCamera.cupdate();
        llxInteractor.setCamera(&llxCamera);
        break;

    default:
        break;
    }

    update();
}

void OglWidget::keyReleaseEvent(QKeyEvent *event){
    switch (event->key()) {
    case Qt::Key_Control:
    case Qt::Key_Shift:
        llxInteractor.setSpeed(1.0f);
        break;

    default:
        break;
    }
    update();
}

void OglWidget::wheelEvent(QWheelEvent *event)
{
    llxInteractor.setScrollDirection(event->delta()>0?true:false);
    update();
}
